home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Java 1996 August
/
Java - Summer 1996.iso
/
kaffe-0.2
/
kaffe
/
gc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-02-19
|
8KB
|
370 lines
/*
* gc.c
* The garbage collector.
*
* Copyright (c) 1996 Systems Architecture Research Centre,
* City University, London, UK.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* Written by Tim Wilkinson <tim@sarc.city.ac.uk>, February 1996.
*/
/* #define NOGC /* Turn off garbage collection */
#define DBG(s)
#define MDBG(s)
#define FDBG(s)
#define ADBG(s)
#include <assert.h>
#include "gtypes.h"
#include "object.h"
#include "classMethod.h"
#include "baseClasses.h"
#include "lookup.h"
#include "thread.h"
#include "gc.h"
#include "md.h"
extern thread* threadQhead[];
extern classes* ThreadClass;
extern strpair* finalpair;
extern thread* finalman;
extern thread* gcman;
static gcRef* refTable[GCREFTABLESZ];
static int refTableIdx;
static gcRef* freeRef;
static gcRef* permList;
static void* minGcMemory = (void*)-1;
static void* maxGcMemory = (void*)0;
static gcRef* garbageObjects;
static unsigned int allocCount;
/* When's a good time to run the GC? Run it every 1000 allocations */
#define GCTRIGCOUNT 1000
static void markObject(object*);
static void garbageCollect(void);
static gcRef* validReference(object*);
/*
* Add an object into the garbage collection scheme.
*/
void
add_object(object* o, bool perm)
{
gcRef* table;
gcRef* ref;
int i;
#if defined(NOGC)
return;
#endif
/* Reset min and max memory pointers */
if ((void*)o < minGcMemory) {
minGcMemory = (void*)o;
}
if ((void*)o > maxGcMemory) {
maxGcMemory = (void*)o;
}
ref = freeRef;
if (ref != 0) {
freeRef = freeRef->next;
}
else {
assert(refTableIdx < GCREFTABLESZ);
table = (gcRef*)malloc(sizeof(gcRef) * GCREFMAX);
assert(table != 0);
/* Build into free list */
for (i = 0; i < GCREFMAX; i++) {
table[i].flags = GC_FREE;
table[i].next = &table[i+1];
table[i].idx = i + (refTableIdx * GCREFMAX);
}
table[GCREFMAX-1].next = 0;
freeRef = &table[1];
ref = &table[0];
refTable[refTableIdx] = table;
refTableIdx++;
}
assert(ref->flags == GC_FREE);
/* Make ref point at object, and object point at ref */
o->idx = ref->idx;
ref->obj = o;
ref->flags = GC_UNMARK;
ref->next = 0;
/* If object is permenant, put it on the perm list */
if (perm == true) {
ref->next = permList;
permList = ref;
}
ADBG( printf("Adding new object 0x%x at index %d - %s\n", o, o->idx, o->type ? o->type->name : "<none>"); )
if (++allocCount % GCTRIGCOUNT == 0) {
invokeGarbageCollector();
}
}
/*
* Invoke the garbage collector (if we have one)
*/
void
invokeGarbageCollector(void)
{
if (gcman != 0) {
lockMutex(&gcman->obj);
signalCond(&gcman->obj);
unlockMutex(&gcman->obj);
}
}
/*
* Is object reference a real object?
*/
static
inline
gcRef*
validReference(object* o)
{
gcRef* ref;
/* Validate object address. To be a real object it must lie
inside the malloced memory pool, and its index must refer to
a GC entry which refers to it. */
if ((void*)o < minGcMemory || (void*)o > maxGcMemory) {
return (0);
}
ref = refTable[(o->idx / GCREFMAX) % GCREFTABLESZ];
if (ref == 0) {
return (0);
}
ref = &ref[o->idx % GCREFMAX];
if (ref->obj != o) {
return (0);
}
/* Stop when we reach a mark */
if (ref->flags != GC_UNMARK) {
return (0);
}
return (ref);
}
/*
* Garbage collect objects.
*/
static
void
garbageCollect(void)
{
int i;
thread* tid;
object** ptr;
gcRef* ref;
int j;
gcRef* table;
#if defined(NOGC)
return;
#endif
DBG( printf("Garbage collecting ... \n"); )
/* Scan the permentant object list */
for (ref = permList; ref != 0; ref = ref->next) {
markObject(ref->obj);
}
/* Scan each thread */
for (i = MIN_THREAD_PRIO; i <= MAX_THREAD_PRIO; i++) {
for (tid = threadQhead[i]; tid != 0; tid = tid->next) {
markObject(&tid->obj);
for (ptr = (object**)tid->eetop->restorePoint; ptr < (object**)tid->eetop->stackEnd; ptr++) {
markObject(*ptr);
}
}
}
/* Now look through object table for objects which have not
been marked. These can be freed. */
for (j = 0; j < refTableIdx; j++) {
table = refTable[j];
for (i = 0; i < GCREFMAX; i++) {
switch(table[i].flags) {
case GC_GARBAGE:
case GC_FREE:
break;
case GC_MARK:
table[i].flags = GC_UNMARK; /* For next time */
break;
case GC_UNMARK:
/* Object not marked - free */
table[i].flags = GC_GARBAGE;
table[i].next = garbageObjects;
garbageObjects = &table[i];
break;
}
}
}
DBG( printf("Garbage collecting ... done.\n"); )
}
/*
* Given an object, mark it and follow any object references it contains.
*/
static
void
markObject(object* o)
{
object** child;
classes* class;
fields* fld;
int i;
gcRef* ref;
/* Null object reference */
if (o == 0) {
return;
}
/* Invalid object or already visited? */
ref = validReference(o);
if (ref == 0) {
return;
}
MDBG( printf("Marking object 0x%x at index %d - %s\n", o, o->idx, o->type ? o->type->name : "<none>"); )
/* Mark this object */
ref->flags = GC_MARK;
class = o->type;
/* Mark the class object */
markObject(&class->head);
/* If this is a class object, mark the static fields and constants */
if (class == ClassClass) {
class = (classes*)o;
markObject(&class->superclass->head);
child = (object**)class->staticFields;
for (i = 0; i < class->sfsize; i++) {
markObject(child[i]);
}
if (class->constants != 0) {
child = (object**)class->constants->data;
for (i = class->constants->size - 1; i >= 0; i--) {
markObject(child[i]);
}
}
}
/* Otherwise, a normal object or an array of objects then ... */
else if (o->type->sig[0] == 'L' || o->type->sig[1] == 'L') {
assert(o->type->sig[0] == 'L' || o->type->sig[0] == '[');
child = (object**)o->data;
for (i = 0; i < o->size; i++) {
markObject(child[i]);
}
}
}
/*
* Finaliser.
* Finalises any objects which have been garbage collected before
* deleting them.
*/
void
finaliserMan(void)
{
object* obj;
gcRef* ref;
void* func;
/* All threads start with interrupts disabled */
intsRestore();
lockMutex(¤tThread->obj);
for (;;) {
while (garbageObjects) {
ref = garbageObjects;
garbageObjects = garbageObjects->next;
assert(ref->flags == GC_GARBAGE);
ref->next = 0;
obj = ref->obj;
/* Finalise object if necessary */
if (obj->final == 0) {
obj->final = 1;
func = findMethod(obj->type, finalpair);
assert(func != 0);
CALL_KAFFE_FUNCTION_VARARGS(func, obj, 0, 0);
/* Don't free it cause it might live */
ref->flags = GC_UNMARK;
continue;
}
/* Special handling for threads */
if (obj->type == ThreadClass) {
thread* stiff = (thread*)obj;
free(stiff->eetop->stackBase);
free(stiff->eetop);
}
/* Don't handle classes yet */
else if (obj->type == ClassClass) {
abort();
}
/* Put entry onto freelist */
ref->flags = GC_FREE;
ref->next = freeRef;
freeRef = ref;
/* Free the object */
FDBG( printf("Freeing object 0x%x(%d) %s\n", obj,
ref->idx, obj->type->name); )
free(obj);
}
waitCond(¤tThread->obj, -1);
}
}
/*
* Garbage collector.
* Run the garbage collector.
*/
void
gcMan(void)
{
/* All threads start with interrupts disabled */
intsRestore();
lockMutex(¤tThread->obj);
for (;;) {
waitCond(¤tThread->obj, -1);
/* Run the garbage collector */
garbageCollect();
/* If there's garbage, finalise it */
if (garbageObjects != 0 && finalman != 0) {
lockMutex(&finalman->obj);
signalCond(&finalman->obj);
unlockMutex(&finalman->obj);
}
}
}